package jsyf.com.dao;

import com.alibaba.fastjson.JSONObject;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;

import javax.persistence.Column;
import javax.sql.DataSource;

import jsyf.com.constant.Const;

/**
 * <B>系统名称：</B>通用系统功能<BR>
 * <B>模块名称：</B>数据访问通用功能<BR>
 * <B>中文类名：</B>基础数据访问<BR>
 * <B>概要说明：</B><BR>
 *
 * @author: @韩武洽 @Wyshown
 * @Version: V1.0.0
 */

public class BaseJdbcDao {

    /**
     * JSON数据行映射器
     */
    private static final JsonRowMapper JSON_ROW_MAPPER = new JsonRowMapper();

    /**
     * JDBC调用模板
     */
    private JdbcTemplate jdbcTemplate;

    /**
     * 启动时间
     */
    private static Date startTime;

    /**
     * Dao层操作
     */
    protected enum OptionState {
        SAVE,
        UPDATE,
        REMOVE
    }

    /**
     * @param dataSource 数据源
     * @Description: 初始化JDBC调用模板  <br/>
     * @return: void
     * @throw:
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    @Autowired
    public void initJdbcTemplate(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        if (startTime == null) {
            startTime = getCurrentTime();
        }
    }

    /**
     * <B>取得：</B>JDBC调用模板<BR>
     *
     * @return JdbcTemplate JDBC调用模板
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    /**
     * <B>取得：</B>启动时间<BR>
     *
     * @return Date 启动时间
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public Date getStartTime() {
        return startTime;
    }

    /**
     * <B>方法名称：</B>获取数据库的当前时间<BR>
     * <B>概要说明：获取当前系统时间,由于mysql默认system_time_zone是CST时区,查询出来的时间和现在的时区相差14个小时,
     *            需要设置mysql的时区问题.按一下步骤设置mysql的时区.
     *            show variables like '%time_zone%'; 显示时区
     *            set GLOBAL time_zone = '+8:00'; 设置全局时区
     *            set  time_zone = '+8:00'; 设置系统时区
     *            FLUSH PRIVILEGES; 刷新mysql
     *            参考:http://www.jianshu.com/p/ea7ef2d29940
     * </B><BR>
     *
     * @return Date 当前时间
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public Date getCurrentTime() {
        return this.getJdbcTemplate().queryForObject("select SYSDATE() FROM DUAL", Date.class);
    }

    /**
     * <B>方法名称：</B>查询JSON列表<BR>
     * <B>概要说明：</B><BR>
     *
     * @param sql  SQL语句
     * @param args 参数
     * @return List<JSONObject> JSON列表
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public List<JSONObject> queryForJsonList(String sql, Object... args) {
        return this.jdbcTemplate.query(sql, JSON_ROW_MAPPER, args);
    }

    /**
     * <B>方法名称：</B>查询JSON数据<BR>
     * <B>概要说明：</B><BR>
     *
     * @param sql  SQL语句
     * @param args 参数
     * @return JSONObject JSON数据
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public JSONObject queryForJsonObject(String sql, Object... args) {
        List<JSONObject> jsonList = queryForJsonList(sql, args);
        if (jsonList == null || jsonList.size() < 1) {
            return null;
        }
        return jsonList.get(0);
    }

    /**
     * <B>方法名称：</B>查询文本<BR>
     * <B>概要说明：</B><BR>
     *
     * @param sql  SQL语句
     * @param args 参数
     * @return String 文本
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public String queryForString(String sql, Object... args) {
        List<String> dataList = this.jdbcTemplate.queryForList(sql, args, String.class);
        if (dataList == null || dataList.size() < 1) {
            return null;
        }
        return dataList.get(0);
    }

    /**
     * <B>方法名称：</B>拼接分页语句<BR>
     * <B>概要说明：</B><BR>
     *
     * @param sql  SQL语句
     * @param page 开始记录页面（1开始）
     * @param rows 每页限制记录数
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public void appendPageSql(StringBuffer sql, int page, int rows) {

        if (Const.INT_ZERO != rows) {
            sql.append(" LIMIT " + (page - 1) * rows + "," + rows);
        }
    }

    /**
     * <B>方法名称：</B>获取唯一键值<BR>
     * <B>概要说明：</B>生成32位的唯一标识, 前12位是添加数据的时间: 如: 201710121706<BR>
     *
     * @return String 唯一键值
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public String generateKey() {
        String sql = "SELECT DATE_FORMAT(SYSDATE(),'%Y%m%d%H%i')";
        String pre = this.getJdbcTemplate().queryForObject(sql, String.class);
        String uid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        return pre + uid.substring(12);
    }

    /**
     * <B>方法名称：</B>用于 in 通配符(?) 的拼接<BR>
     * <B>概要说明：</B>字段 in(?,?,?,?,?)<BR>
     *
     * @param sql     sql
     * @param sqlArgs 参数容器
     * @param params  参数的个数
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public void appendSqlIn(StringBuffer sql, List<Object> sqlArgs, String[] params) {
        if (params != null && params.length > 0) {
            sql.append(" (");
            for (int i = 0; i < params.length; i++) {
                if (i == 0) {
                    sql.append("?");
                } else {
                    sql.append(",?");
                }
                sqlArgs.add(params[i]);
            }
            sql.append(") ");
        }
    }

    /**
     * <B>方法名称：</B>适应SQL列名<BR>
     * <B>概要说明：</B><BR>
     *
     * @param c 原列名
     * @return String 调整后列名
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public static String c(String c) {
        if (StringUtils.isBlank(c)) {
            return null;
        }
        return c.trim().toUpperCase();
    }

    /**
     * <B>方法名称：</B>适应SQL参数<BR>
     * <B>概要说明：</B>防止SQL注入问题<BR>
     *
     * @param v 参数
     * @return String 调整后参数
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    public static String v(String v) {
        if (StringUtils.isBlank(v)) {
            return null;
        }
        return v.trim().replaceAll("'", "''");
    }

    /**
     * <B>方法名称：</B>获取日期文本值<BR>
     * <B>概要说明：</B><BR>
     *
     * @param rs     结果集
     * @param column 列名
     * @return String 文本值
     * @throws SQLException SQL异常错误
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    protected String getDate(ResultSet rs, String column) throws SQLException {
        Date date = rs.getDate(column);
        if (date == null) {
            return null;
        }
        return DateFormatUtils.format(date, Const.FORMAT_DATE);
    }

    /**
     * <B>方法名称：</B>获取日期时间文本值<BR>
     * <B>概要说明：</B><BR>
     *
     * @param rs     结果集
     * @param column 列名
     * @return String 文本值
     * @throws SQLException SQL异常错误
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    protected String getDateTime(ResultSet rs, String column) throws SQLException {
        Date date = rs.getDate(column);
        if (date == null) {
            return null;
        }
        return DateFormatUtils.format(date, Const.FORMAT_DATETIME);
    }

    /**
     * <B>方法名称：</B>获取时间戳文本值<BR>
     * <B>概要说明：</B><BR>
     *
     * @param rs     结果集
     * @param column 列名
     * @return String 文本值
     * @throws SQLException SQL异常错误
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    protected String getTimestamp(ResultSet rs, String column) throws SQLException {
        Date date = rs.getDate(column);
        if (date == null) {
            return null;
        }
        return DateFormatUtils.format(date, Const.FORMAT_TIMESTAMP);
    }

    /**
     * <B>方法名称：</B>单表INSERT方法<BR>
     * <B>概要说明：</B>单表INSERT方法<BR>
     *
     * @param tableName 表名
     * @param data      JSONObject对象
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    protected int insert(String tableName, JSONObject data) {

        if (data.size() <= 0) {
            return 0;
        }

        StringBuffer sql = new StringBuffer();
        sql.append(" INSERT INTO ");
        sql.append(tableName + " ( ");

        Set<Entry<String, Object>> set = data.entrySet();
        List<Object> sqlArgs = new ArrayList<Object>();
        for (Iterator<Entry<String, Object>> iterator = set.iterator(); iterator.hasNext(); ) {
            Entry<String, Object> entry = iterator.next();
            sql.append(entry.getKey() + ",");
            if (entry.getValue() == null){
                sqlArgs.add(entry.getValue());
            } else {
                sqlArgs.add(entry.getValue().equals("") ? null : entry.getValue());
            }
        }

        sql.delete(sql.length() - 1, sql.length());
        sql.append(" ) VALUES ( ");
        for (int i = 0; i < set.size(); i++) {
            sql.append("?,");
        }

        sql.delete(sql.length() - 1, sql.length());
        sql.append(" ) ");

        return this.getJdbcTemplate().update(sql.toString(), sqlArgs.toArray());
    }

    /**
     * @param tableName
     * @param data
     * @param id
     * @Description: 更新数据库方法  <br/>
     * @return: int
     * @throw:
     * @author: 徐波
     * @createTime: 2017/10/19 0019 10:39
     * @Version: V1.0.0
     */
    protected int update(String tableName, JSONObject data, JSONObject id) {

        if (data.size() <= 0) {
            return 0;
        }

        StringBuffer sql = new StringBuffer();
        sql.append(" UPDATE ");
        sql.append(tableName + " SET ");

        Set<Entry<String, Object>> set = data.entrySet();
        for (Iterator<Entry<String, Object>> iterator = set.iterator(); iterator.hasNext(); ) {
            Entry<String, Object> entry = iterator.next();
            if (entry.getValue() != null){
//                sql.append(entry.getKey() + "="+entry.getValue()+",");
//            } else {
                sql.append(entry.getKey() + "="+(entry.getValue().equals("") ? null : "'"+entry.getValue()+"'")+",");
            }
        }
        sql.delete(sql.length() - 1, sql.length());
        sql.append(" WHERE 1=1");
        Set<String> keys = id.keySet();
        Iterator<String> keyIterator = keys.iterator();
        while (keyIterator.hasNext()) {
            String condition = keyIterator.next();
            sql.append(" AND " + condition + "=" + "'" + id.get(condition) + "'");
        }
        System.out.println(sql.toString());
        return this.getJdbcTemplate().update(sql.toString());
    }

    /**
     * @param deleteSql 删除预处理sql语句
     * @param params    参数
     * @Description: 物理删除的方法, 慎用  <br/>
     * @return: int
     * @throw:
     * @author: zhangxiaoman
     * @createTime: 2017/10/26 19:16
     * @Version: V1.0.0
     */
    public int delete(String deleteSql, final String[] params) {
        return this.getJdbcTemplate().update(deleteSql, params);
    }

    /**
     * <B>方法名称：</B>批量新增數據方法<BR>
     * <B>概要说明：</B><BR>
     *
     * @param tableName 数据库表名称
     * @param list      插入数据集合
     * @author: @韩武洽 @Wyshown
     * @Version: V1.0.0
     */
    protected void insertBatch(String tableName, final List<LinkedHashMap<String, Object>> list) {

        if (list.size() <= 0) {
            return;
        }

        LinkedHashMap<String, Object> linkedHashMap = list.get(0);

        StringBuffer sql = new StringBuffer();
        sql.append(" INSERT INTO ");
        sql.append(tableName + " ( ");

        final String[] keyset = linkedHashMap.keySet().toArray(new String[linkedHashMap.size()]);

        for (int i = 0; i < linkedHashMap.size(); i++) {
            sql.append(keyset[i] + ",");
        }

        sql.delete(sql.length() - 1, sql.length());

        sql.append(" ) VALUES ( ");
        for (int i = 0; i < linkedHashMap.size(); i++) {
            sql.append("?,");
        }

        sql.delete(sql.length() - 1, sql.length());
        sql.append(" ) ");

        this.getJdbcTemplate().batchUpdate(sql.toString(), new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                LinkedHashMap<String, Object> map = list.get(i);
                Object[] valueset = map.values().toArray(new Object[map.size()]);
                int len = map.keySet().size();
                for (int j = 0; j < len; j++) {
                    ps.setObject(j + 1, valueset[j]);
                }
            }

            @Override
            public int getBatchSize() {
                return list.size();
            }
        });
    }


    /**
     * @param obj   添加修改时的实体类对象
     * @param clazz 类名: 实体类.class
     * @Description: 方法描述, 必填  <br/>
     * @return: 根据实体类 返回和实体类对应的jsonObject字段
     * @author: @韩武洽 @Wyshown
     * @createTime: 2017/10/25 下午12:33
     * @Version: V1.0.0
     */
    public static JSONObject getJsonObjectByEntity(JSONObject obj, Class<?> clazz) throws Exception {
        JSONObject resultJson = new JSONObject();
        Method[] methods = clazz.getMethods();

        try {
            String entityName = "";
            // 进行循环
            for (Method method : methods) {
                // 判断方法上是否存在Column这个注解
                if (method.isAnnotationPresent(Column.class)) {
                    Column col = method.getAnnotation(Column.class);
                    entityName = subStrMethodNameToEntityName(method.getName());
                    if (obj.containsKey(entityName)) {
                        resultJson.put(col.name(), obj.get(entityName));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return resultJson;
    }

    /**
     * @param methodName 方法的名称 如: getUserName
     * @Description: 去掉方法中的get 如: getUserName --> UserName <br/>
     * @return: 返回 不包含get的方法名称
     * @author: @韩武洽 @Wyshown
     * @createTime: 2017/10/25 下午12:31
     * @Version: V1.0.0
     */
    public static String subStrMethodNameToEntityName(String methodName) {
        return methodName.substring(3, 4).toLowerCase() + methodName.substring(4, methodName.length());
    }


    /**
     * 把以","隔开的字符串转化为一个List
     *
     * @param str= "12,23,34"
     * @return
     */
    public static List<String> stringToList(String str) {
        if (str == null) {
            return null;
        }
        String d[] = str.split(",");
        List<String> list = new ArrayList<String>();

        for (int i = 0; i < d.length; i++) {
            list.add(d[i]);
        }
        return list;
    }

    /**
     * 把以"|"隔开的字符串转化为一个List
     *
     * @param str= "12,23,34"
     * @return
     */
    public static List<String> stringToLists(String str) {
        if (str == null) {
            return null;
        }
        String d[] = str.split("\\|");
        List<String> list = new ArrayList<String>();

        for (int i = 0; i < d.length; i++) {
            list.add(d[i]);
        }
        return list;
    }

    /**
     * @param sequenceId 表的自增长序列主键
     * @param tableName 表名
     * @Description:  根据表的自增长序列值获得对应的唯一32位键值 <br/>
     * @return: java.lang.String
     * @author: @张晓满 @Tupurp
     * @createTime: 2017/12/5 15:00
     * @Version: V1.0.0
     */
    public String getIdBySequenceId(String sequenceId,String tableName){
        StringBuffer sql = new StringBuffer();
        sql.append(" SELECT \n");
        sql.append(" ID \n");
        sql.append(" FROM \n");
        sql.append(tableName);
        sql.append(" WHERE 1 = 1 \n");
        sql.append(" AND SEQUENCE_ID = ?");

        return queryForString(sql.toString(),sequenceId);
    }


    /**
     * jsyf.com.dao.BaseJdbcDao.insertBatchToMoreData
     * @param tableName 批量新增的表名称
     * @param list 批量新增的Map数组
     * @Description: 批量插入多条数据，通过把需要批量新增的SQL拼接出来，通过？站位符，对当前数值站位，
     *                  然后向数组中添加此次占位符数值。<br/>
     * @return: void
     * @author: 张伟
     * @createTime: 2017/12/18 18:07
     * @Version: V1.0.0
     */
    protected void insertBatchToMoreData(String tableName, final List<? extends Map> list) {
        if (list.size() <= 0) {
            return;
        }
        Map<String, Object> dataMap = list.get(0);
        StringBuffer sql = new StringBuffer();
        /*添加插入的字段名称*/
        sql.append(" INSERT INTO ");
        sql.append(tableName + " ( ");
        dataMap.forEach((k,v) ->{
            sql.append("  " + k +" ,");
        });
        sql.delete(sql.length()-1, sql.length());
        sql.append(" ) VALUES  ");
        ArrayList<Object> sqlArgs = new ArrayList<>();
        /*添加？占位符，并且给每集合添加占位符数值*/
        list.forEach(map ->{
            sql.append(" (  ");
            map.forEach((k,v) ->{
                sql.append("  ? ,");
                sqlArgs.add(v);
            });
            sql.delete(sql.length()-1, sql.length());
            sql.append(" ) ");
            if (list.indexOf(map) != list.size() - 1) {
                sql.append(",");
            }
        });
        this.getJdbcTemplate().update(sql.toString(), sqlArgs.toArray());
    }

    /**
     * jsyf.com.dao.BaseJdbcDao#getGenerateKey(java.util.Date)
     * @param currentTime
     * @Description: 通过获取当前时间，生成id  <br/>
     * @return: java.lang.String
     * @author: 张伟
     * @createTime: 2017/12/18 18:50
     * @Version: V1.0.0
     */
    public String getGenerateKey(Date currentTime) {
        //设置日期格式
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmm");
        // 当前系统时间
        String pre=df.format(currentTime);
        String uid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        return pre + uid.substring(12);
    }
}
